package cn.powertime.evaluation.shiro.jwt;

import cn.powertime.evaluation.shiro.exception.TokenParserException;
import cn.powertime.evaluation.vo.ShiroUserInfo;
import com.google.common.collect.Maps;
import io.jsonwebtoken.*;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import java.util.Arrays;
import java.util.Date;
import java.util.Map;
import java.util.Optional;

/**
 * @author ZYW
 */
@Component
public class TokenUtils {
    private static final String AUDIENCE_UNKNOWN = "unknown";
    private static final String AUDIENCE_WEB = "web";
    private static final String AUDIENCE_MOBILE = "mobile";
    private static final String AUDIENCE_TABLET = "tablet";


    public static final String IS_SUPER_ADMIN = "sa";
    public static final String USER_ID = "ui";
    public static final String CREATED = "created";

    @Value("${noopsyche.token.secret:powertime.cn}")
    private String secret;
    @Value("${noopsyche.token.expiration:86400}")
    private Long expiration;


    private Claims getClaimsFromToken(String token) {
        try {
            return Jwts.parser()
                    .setSigningKey(this.secret)
                    .parseClaimsJws(token)
                    .getBody();
        } catch (ExpiredJwtException e) {
            throw new TokenParserException("token已过期。");
        } catch (UnsupportedJwtException e) {
            throw new TokenParserException("token非法。");
        } catch (MalformedJwtException e) {
            throw new TokenParserException("jwt未能正常被构造。");
        } catch (SignatureException e) {
            throw new TokenParserException("签名计算失败。");
        } catch (IllegalArgumentException e) {
            throw new TokenParserException("token参数非法。");
        }
    }

    public String generateToken(String username,Long userId) {
        Map<String, Object> claims = Maps.newHashMap();
        claims.put(Claims.SUBJECT, username);
        claims.put(USER_ID,userId);
        //claims.put(IS_SUPER_ADMIN,isSuperAdmin);
        /*this.generateAudience(device)*/
        claims.put(Claims.AUDIENCE, AUDIENCE_WEB);
        claims.put(CREATED, new Date(System.currentTimeMillis()));
        return this.generateToken(claims);
    }

    private String generateToken(Map<String, Object> claims) {
        return Jwts.builder()
                .setClaims(claims)
                .setExpiration(new Date(System.currentTimeMillis() + this.expiration * 1000))
                .signWith(SignatureAlgorithm.HS512, this.secret)
                .compact();
    }


    public String getUsernameFromToken(String token) {
        return this.getClaimsFromToken(token).getSubject();
    }

    public ShiroUserInfo getUserIdAndIsSuperAdmin(String token) {
        Claims claims = this.getClaimsFromToken(token);
        return ShiroUserInfo.builder()
                .id(Long.valueOf(String.valueOf(claims.get(USER_ID))))
                .username(String.valueOf(claims.get(Claims.SUBJECT)))
                //.superAdmin((Boolean) claims.get(IS_SUPER_ADMIN))
                .build();
    }

    public String refreshToken(String token) {
        final Claims claims = this.getClaimsFromToken(token);
        claims.put("created", new Date(System.currentTimeMillis()));
        return this.generateToken(claims);
    }

    public static String getRequestToken(HttpServletRequest httpRequest) {
        // 先从Header里面获取
        String token = httpRequest.getHeader("token");
        if(StringUtils.isBlank(token)){
            // 获取不到再从Parameter中拿 为了兼容目前的api-docs支持
            token = httpRequest.getParameter("token");
            // 还是获取不到再从Cookie中拿
            if(StringUtils.isBlank(token)){
                Cookie[] cookies = Optional.ofNullable(httpRequest.getCookies()).orElse(new Cookie[]{});
                Optional<Cookie> optToken = Arrays.stream(cookies)
                        .filter(cookie -> StringUtils.equalsIgnoreCase(cookie.getName(), "token"))
                        .findFirst();
                if(optToken.isPresent()) {
                    token = optToken.get().getValue();
                }
            }
        }
        return token;
    }

    public static String getRequestTokens(HttpServletRequest httpRequest) {
        // 先从Header里面获取
        String token = httpRequest.getHeader("token");
        if(StringUtils.isBlank(token)){
            // 获取不到再从Parameter中拿 为了兼容目前的api-docs支持
            token = httpRequest.getParameter("token");
            // 还是获取不到再从Cookie中拿
            if(StringUtils.isBlank(token)){
                Cookie[] cookies = Optional.ofNullable(httpRequest.getCookies()).orElse(new Cookie[]{});
                Optional<Cookie> optToken = Arrays.stream(cookies)
                        .filter(cookie -> StringUtils.equalsIgnoreCase(cookie.getName(), "token"))
                        .findFirst();
                if(optToken.isPresent()) {
                    token = optToken.get().getValue();
                }
            }
        }else {
            throw new TokenParserException("token为空");
        }
        return token;
    }
    /**
     * shiro放行接口，手动获取用户id
     * @param request
     * @return
     */
    public Long getUid(HttpServletRequest request){
        String token = TokenUtils.getRequestToken(request);
        Long uid;
        if(StringUtils.isBlank(token)){
            uid = 0L;
        }else{
            ShiroUserInfo shiroUserInfo = getUserIdAndIsSuperAdmin(token);
            uid = shiroUserInfo.getId();
        }
        return uid;
    }

}
